﻿/* Hangeul Typing Behavior Analyzer 2.0
 * created by Kim Yongmook ( http://moogi.new21.org )
 * Originically created on March 7, 2000
 * Modified on April 21, 2005
 * Ver 2.0 released by Pat-al ( http://pat.im ) on November 27, 2011 
 * Ver 2.01 released by Pat-al on December 11, 2011
 * Ver 3.0 developed by SaschaNaz ( http://cyberkagami.wordpress.com/ ) on ...
 * 
 * 용어
 * Freq=잦기=Frequency(빈도)
 * Natnae=낱내=음절/소리마디
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HangulTyperRP
{
    class Processor
    {
        /// <summary>
        /// Member: 0->Shift?, 1->Finger, 2->Height in keyboard, 3->Left/Right relative key position
        /// </summary>
        UInt16[,] typeinfo = new UInt16[128, 4];

        /// <summary>
        /// n:문자, a:shift(0/1), b:손가락+- c:줄(아래부터 1~5) d:좌우 상대 위치
        /// </summary>
        /// <param name="n">문자</param>
        /// <param name="a">shift</param>
        /// <param name="b">손가락</param>
        /// <param name="c">줄</param>
        /// <param name="d">좌우 상대 위치</param>
        void Sett(Int32 n, Int32 a, Int32 b, Int32 c, Int32 d)
        {
            typeinfo[n, 0] = (UInt16)a;
            typeinfo[n, 1] = (UInt16)b;
            typeinfo[n, 2] = (UInt16)c;
            typeinfo[n, 3] = (UInt16)d;
        }
        Boolean IsHanja(Char ch1, Char ch2)
        {
            return (ch1 >= 0xE0 && ch1 <= 0xF9 && ((ch2 >= 0x31 && ch2 <= 0x7E) || (ch2 > 0x91 && ch2 <= 0xFE)));
        }

        Int32[] KeyUntreatedCharacterTotal;//자판 배열에 없어서 처리하지 못한 문자
        Int32[] KeySetType;//벌식(2,3)
        String[] KeyFingering;//운지법 - 키보드 타입 저장소로 쓰이는 듯하다. 키보드 타입 이름이 문자열로 저장된다.

        void InitTypeInfo(Int32 keyLayoutNum)
        {
            Int32 i;

            Sett('a', 0, -5, 3, -5); Sett('s', 0, -4, 3, -4); Sett('d', 0, -3, 3, -3); Sett('f', 0, -2, 3, -2); Sett('g', 0, -2, 3, -1);
            Sett('h', 0, 2, 3, 1); Sett('j', 0, 2, 3, 2); Sett('k', 0, 3, 3, 3); Sett('l', 0, 4, 3, 4);

            Sett('q', 0, -5, 4, -5); Sett('w', 0, -4, 4, -4); Sett('e', 0, -3, 4, -3); Sett('r', 0, -2, 4, -2); Sett('t', 0, -2, 4, -1);
            Sett('y', 0, 2, 4, 1); Sett('u', 0, 2, 4, 2); Sett('i', 0, 3, 4, 3); Sett('o', 0, 4, 4, 4); Sett('p', 0, 5, 4, 5);
            Sett('z', 0, -5, 2, -5); Sett('x', 0, -4, 2, -4); Sett('c', 0, -3, 2, -3); Sett('v', 0, -2, 2, -2);	// 표준식 왼쪽 밑글쇠 치기

            if (KeyFingering[keyLayoutNum] != "Gong" || KeyFingering[keyLayoutNum] != "Gong3")
            {
                Sett('z', 0, -4, 2, -4); Sett('x', 0, -3, 2, -3); Sett('c', 0, -2, 2, -2); Sett('v', 0, -2, 2, -2);	// 공병우 자판식 왼쪽 밑글쇠 치기 (공 운지법)
            }

            Sett('b', 0, -2, 2, -1);
            if (KeySetType[keyLayoutNum] == 2 && KeyFingering[keyLayoutNum] != "Pyojun")
                Sett('b', 0, 2, 2, 1);	// 'ㅠ'(B 자리)를 오른손으로 치기 (두벌 자판)
            Sett('n', 0, 2, 2, 1); Sett('m', 0, 2, 2, 2);
            for (i = 'A'; i <= 'Z'; i++)
                Sett(i, 1, typeinfo[i + 32, 1], typeinfo[i + 32, 2], typeinfo[i + 32, 3]);

            Sett('1', 0, -5, 5, -5); Sett('2', 0, -4, 5, -4); Sett('3', 0, -3, 5, -3); Sett('4', 0, -2, 5, -2); Sett('5', 0, -2, 5, -1);
            Sett('6', 0, 2, 5, 1); Sett('7', 0, 2, 5, 2); Sett('8', 0, 3, 5, 3); Sett('9', 0, 4, 5, 4); Sett('0', 0, 5, 5, 5);

            Sett('!', 1, -5, 5, -5); Sett('@', 1, -4, 5, -4); Sett('#', 1, -3, 5, -3); Sett('$', 1, -2, 5, -2); Sett('%', 1, -2, 5, -1);
            Sett('^', 1, 2, 5, 1); Sett('&', 1, 2, 5, 2); Sett('*', 1, 3, 5, 3); Sett('(', 1, 4, 5, 4); Sett(')', 1, 5, 5, 5);

            if (KeySetType[keyLayoutNum] == 3 && KeyFingering[keyLayoutNum] != "Gong3")
            {	// 공병우 계열 3벌 자판은 'ᅣ'(6 자리) 글쇠를 왼손으로 침
                Sett('6', 0, -2, 5, 1); Sett('^', 1, -2, 5, 1);
            }

            Sett('`', 0, -5, 5, -6); Sett('-', 0, 5, 5, 6); Sett('=', 0, 5, 5, 7);
            Sett('~', 1, -5, 5, -6); Sett('_', 1, 5, 5, 6); Sett('+', 1, 5, 5, 7);
            Sett('[', 0, 5, 4, 6); Sett(']', 0, 5, 4, 7); Sett('\\', 0, 5, 4, 8);
            Sett('{', 1, 5, 4, 6); Sett('}', 1, 5, 4, 7); Sett('|', 1, 5, 4, 8);
            Sett(';', 0, 5, 3, 5); Sett('\'', 0, 5, 3, 6);
            Sett(':', 1, 5, 3, 5); Sett('\"', 1, 5, 3, 6);
            Sett(',', 0, 3, 2, 3); Sett('.', 0, 4, 2, 4); Sett('/', 0, 5, 2, 5);
            Sett('<', 1, 3, 2, 3); Sett('>', 1, 4, 2, 4); Sett('?', 1, 5, 2, 5);

            Sett('\n', 0, 5, 3, 7); Sett(' ', 0, 1, 1, 0);
            //Sett('문자',shift,'-+손가락',줄,좌우 상대 위치)   Sett(n,a,b,c) typeinfo[n][0]=a, typeinfo[n][1]=b, typeinfo[n][2]=c;
        }

        String Parse(String keyseq, Int32 keyLayoutNum)
        {
            String outputText = "";

            Int32 CountedEnter = 0, CountedSpace = 0;//줄바꿈, 빈 칸을 셈한 수
            Int32 LeftGeneralKeyStroke = 0, RightGeneralKeyStroke = 0;//일반 글쇠 누르는 수
            Int32 LeftShiftStroke = 0, RightShiftStroke = 0;//shift 누르는 새끼 손가락 치는 수
            Int32 TotalGeneralKeyStroke = 0;

            Int32 PrevHand = 0, PrevFinger = -1, PrevLine = -1; //앞에 누른 글쇠 상태

            Int32 ShiftHand = 0, ShiftFinger = -1;//shift 누른 글쇠, 손
            Int32 PrevShiftHand = 0, PrevShiftFinger = -1;//shift 누른 글쇠, 손

            Int32 TotalShiftStroke = 0;//윗글쇠 누른 수, 같은 글쇠 거듭치기
            Int32 LeftShiftSameFingerStroke = 0, RightShiftSameFingerStroke = 0;//손가락 거듭치기에 들어간 윗글쇠 타수
            Int32 LeftSameFingerStroke = 0, RightSameFingerStroke = 0;//같은 손가락 거듭치기
            Int32 TotalSameKeyStroke = 0, LeftSameKeyStroke = 0, RightSameKeyStroke = 0;//같은 글쇠 거듭치기
            Int32 LeftSameHandStrokeState = 0, RightSameHandStrokeState = 0;//같은 손으로 거듭 친 수(상태 변수)
            Int32 LeftHandStrokeState = 0, RightHandStrokeState = 0;//한 손에서 거듭 이어치는 타수

            Int32[] FingerStrokeDistrib = new Int32[10]; FingerStrokeDistrib[0] = 0;//손가락 쓴 수 (0:왼손 새끼, 9:오른손 새끼)
            Int32[,] FingerLineFreq = new Int32[10, 6]; FingerLineFreq[0, 0] = 0;//손가락(finger), 줄(line)에 따른 글쇠 빈도
            Int32[,] GeneralKeyPositionFreq = new Int32[10, 6]; GeneralKeyPositionFreq[0, 0] = 0;//손가락(finger), 줄(line)에 따른 글쇠 빈도
            Int32[] SameFingerStrokeDistrib = new Int32[1] { 0, };//각 손가락에서 거듭치기가 일어난 수

            Int32[,] ContinuousSameHandStroke = new Int32[2, 100];//각 손가락에서 거듭치기가 일어난 빈도 수 [왼손 0, 오른손 1][거듭 친 수-1]
            Int32[,] ContinuousSameFingerStroke = new Int32[10, 100];//각 손가락에서 거듭치기가 일어난 빈도 수 [손가락 0~9][거듭 친 수-1]
            Int32[,] ContinuousSameKeyStroke = new Int32[10, 100];//각 손가락에서 거듭치기가 일어난 빈도 수 [손가락 0~9][거듭 친 수-1]

            for (Int32 i = 0; i < keyseq.Length; i++)
            {
                Int32 Hand, Finger, FingerPosition, Line;//지금 누르는 글쇠 상태: hand - 누른 손, finger - 누른 손가락

                if (i != 0 && (keyseq[i] == ' ' || keyseq[i] == '\n') && keyseq[i] == keyseq[i - 1])
                    continue;//거듭 나오는 빈칸이나 빈줄은 하나만 셈한다.
                if (keyseq[i] == 0x18)
                {
                    PrevHand = 0; PrevFinger = -1;
                    PrevLine = -1;
                    PrevShiftHand = 0; PrevShiftFinger = -1;
                    continue;//자판 배열에 없는 문자는 건너뛴다.
                }
                if (keyseq[i] == ' ') CountedSpace++;
                else if (keyseq[i] == '\n') CountedEnter++;

                Hand = typeinfo[keyseq[i], 1] < 0 ? -1 : 1;//왼손(-) 오른손(+)
                Finger = typeinfo[keyseq[i], 1] < 0 ? typeinfo[keyseq[i], 1] + 5 : typeinfo[keyseq[i], 1] + 4;//왼-오른쪽을 구분하는 손가락 번호 (왼손 다섯째:0, 왼손 첫째:4, 오른손 첫째:5, 오른손 다섯째:9)
                FingerPosition = typeinfo[keyseq[i], 3] < 0 ? typeinfo[keyseq[i], 3] + 5 : typeinfo[keyseq[i], 3] + 4;//첫째 손가락을 빼고, 둘째 손가락 자리를 더 구분하는 손가락 자리 번호
                if (FingerPosition < 0) FingerPosition = 0;
                else if (FingerPosition > 9) FingerPosition = 9;
                Line = typeinfo[keyseq[i], 2];

                if (keyseq[i] != '\n') GeneralKeyPositionFreq[FingerPosition, Line]++;//줄바꾸개를 빼고 둘째 손가락 자리를 더 구분하는 일반 글쇠의 위아래/손가락별 타수를 더한다.
                FingerStrokeDistrib[Finger]++;//손가락별 타수를 셈한다.
                FingerLineFreq[Finger, Line]++;//손가락/위아래 위치 타수를 셈한다.

                TotalGeneralKeyStroke++;
                if (keyseq[i] != ' ' && keyseq[i] != '\n')
                {
                    if (Hand < 0) LeftGeneralKeyStroke++;
                    else if (Hand > 0) RightGeneralKeyStroke++;
                }

                if (typeinfo[keyseq[i], 0] != 0)//shift를 누를 때
                {
                    ShiftHand = -Hand;//shift를 누르는 손
                    ShiftFinger = ShiftHand == 1 ? 9 : 0;//shift를 누르는 새끼 손가락
                    TotalShiftStroke++;//shift 누르는 횟수
                    if (ShiftHand < 0)//일반 글쇠 맞은편의 shift를 치는 것을 반영한다.
                    {
                        LeftShiftStroke++; FingerStrokeDistrib[0]++; FingerLineFreq[0, 2]++; /*general_key_position_freq[0][2];*/
                    }
                    else
                    {
                        RightShiftStroke++; FingerStrokeDistrib[9]++; FingerLineFreq[9, 2]++;
                    }
                }
                else
                {
                    ShiftHand = 0;
                    ShiftFinger = -1;
                }

                Int32 LeftFinger = -1, RightFinger = -1;//치는 손가락 번호
                Int32 SameFinger = -1;//, ShiftSameFingerStrokeFinger = -1;//일반 글쇠와 윗글쇠에서 거듭치기가 일어나는 손가락
                //Int32 PrevSameFinger = -1;//거듭치기가 일어났던 손가락

                if (PrevFinger == Finger)//일반 글쇠를 한 손가락으로 거듭 칠 때
                {
                    SameFinger = Finger;
                    if (Hand < 0)
                    {
                        LeftSameHandStrokeState++; LeftSameFingerStroke++; LeftFinger = Finger; SameFingerStrokeDistrib[Finger]++;
                    }
                    else
                    {
                        RightSameHandStrokeState++; RightSameFingerStroke++; RightFinger = Finger; SameFingerStrokeDistrib[Finger]++;
                    }

                    if (typeinfo[keyseq[i], 2] == typeinfo[keyseq[i - 1], 2] && typeinfo[keyseq[i], 3] == typeinfo[keyseq[i - 1], 3])
                    {
                        TotalSameKeyStroke++;//같은 글쇠 거듭치기
                        if (Hand < 0)//왼손으로 칠 때
                        {
                            for (Int32 i2 = 0; i2 < LeftSameHandStrokeState; i2++)
                            {
                                if (typeinfo[keyseq[i], 2] != typeinfo[keyseq[i - i2], 2] || typeinfo[keyseq[i], 3] != typeinfo[keyseq[i - i2], 3])
                                    break;
                                LeftSameKeyStroke++;
                                ContinuousSameKeyStroke[LeftFinger, i2]++;
                                if (LeftSameHandStrokeState > 1)
                                    ContinuousSameKeyStroke[LeftFinger, i2 - 1]--;
                            }
                        }
                        else//오른손으로 칠 때
                        {
                            for (Int32 i2 = 0; i2 < RightSameHandStrokeState; i2++)
                            {
                                if (typeinfo[keyseq[i], 2] != typeinfo[keyseq[i - i2], 2] || typeinfo[keyseq[i], 3] != typeinfo[keyseq[i - i2], 3])
                                    break;
                                RightSameKeyStroke++;
                                ContinuousSameKeyStroke[RightFinger, i2]++;
                                if (RightSameHandStrokeState > 1)
                                    ContinuousSameKeyStroke[RightFinger, i2 - 1]--;
                            }
                        }
                    }
                }
                if (PrevShiftHand != 0 && Finger == PrevShiftFinger)//윗글쇠를 눌렀던 손가락으로 일반 글쇠를 누를 때
                {
                    if (PrevShiftHand < 0)
                        LeftShiftSameFingerStroke++;
                    else
                        RightShiftSameFingerStroke++;

                    if (Hand < 0)
                    {
                        LeftSameHandStrokeState++;
                        LeftSameFingerStroke++;
                        LeftFinger = Finger;
                    }
                    else
                    {
                        RightSameHandStrokeState++;
                        RightSameFingerStroke++;
                        RightFinger = Finger;
                    }
                    SameFingerStrokeDistrib[Finger]++;
                }
                if (ShiftHand != 0 && PrevFinger == ShiftFinger)//일반 글쇠를 눌렀던 손가락으로 윗글쇠를 누를 때
                {
                    if (ShiftHand < 0)
                    {
                        LeftShiftSameFingerStroke++;
                        LeftSameHandStrokeState++;
                        LeftSameFingerStroke++;
                        LeftFinger = PrevFinger;
                    }
                    else
                    {
                        RightShiftSameFingerStroke++;
                        RightSameHandStrokeState++;
                        RightSameFingerStroke++;
                        RightFinger = PrevFinger;
                    }
                    SameFingerStrokeDistrib[PrevFinger]++;
                }

                if (ShiftHand != 0 && PrevShiftHand != 0 && ShiftHand == PrevShiftHand && ShiftFinger == PrevShiftFinger)
                {
                    TotalShiftStroke--;//윗글쇠를 누르고 있을 때는 윗글쇠 타수를 더하지 않는다.
                    if (ShiftHand < 0)
                    {
                        FingerStrokeDistrib[0]--;
                        FingerLineFreq[0, 2]--;
                        LeftShiftStroke--;
                    }
                    else
                    {
                        FingerStrokeDistrib[9]--;
                        FingerLineFreq[9, 2]--;
                        RightShiftStroke--;
                    }
                }

                if (Hand < 0 || (ShiftHand < 0 && ShiftHand != PrevShiftHand))//왼손으로 칠 때 왼손 타수를 셈한다.
                {
                    ContinuousSameHandStroke[0, LeftHandStrokeState]++;
                    if (LeftHandStrokeState > 0)
                        ContinuousSameHandStroke[0, LeftHandStrokeState - 1]--;
                    LeftHandStrokeState++;
                }
                else LeftHandStrokeState = 0;

                if (Hand > 0 || (ShiftHand > 0 && ShiftHand != PrevShiftHand))//오른손으로 칠 때 오른손 타수를 셈한다.
                {
                    ContinuousSameHandStroke[1, RightHandStrokeState]++;
                    if (RightHandStrokeState > 0)
                        ContinuousSameHandStroke[1, RightHandStrokeState - 1]--;
                    RightHandStrokeState++;
                }
                else RightHandStrokeState = 0;

                if (LeftFinger == -1)
                    LeftSameHandStrokeState = 0;//왼손으로 치지 않았다면 왼손 거듭치기 상태 변수를 초기값으로 돌려 놓는다.
                else
                {
                    ContinuousSameFingerStroke[LeftFinger, LeftSameHandStrokeState]++;
                    if (LeftSameHandStrokeState > 1)
                        ContinuousSameFingerStroke[LeftFinger, LeftSameHandStrokeState - 1]--;
                }

                if (RightFinger == -1)
                    RightSameHandStrokeState = 0;//오른손으로 치지 않았다면 오른손 거듭치기 상태 변수를 초기값으로 돌려 놓는다.
                else
                {
                    ContinuousSameFingerStroke[RightFinger, RightSameHandStrokeState]++;
                    if (RightSameHandStrokeState > 1)
                        ContinuousSameFingerStroke[RightFinger, RightSameHandStrokeState - 1]--;
                }

                PrevHand = Hand; PrevFinger = Finger; PrevLine = typeinfo[keyseq[i], 2];
                PrevShiftHand = ShiftHand;
                PrevShiftFinger = ShiftFinger;
            }

            Int32 LeftKeyStroke = LeftGeneralKeyStroke + LeftShiftStroke;
            Int32 RightKeyStroke = RightGeneralKeyStroke + RightShiftStroke + CountedEnter;
            outputText += String.Format("General-key strokes(L/R): {0} ({1}/{2}) ({3:F1}%/{4:F1}%)",
                TotalGeneralKeyStroke, 
                LeftGeneralKeyStroke, 
                (LeftGeneralKeyStroke * 100d) / (LeftGeneralKeyStroke + RightGeneralKeyStroke),
                (RightGeneralKeyStroke * 100d) / (LeftGeneralKeyStroke + RightGeneralKeyStroke));
            outputText += Environment.NewLine
                + String.Format("Shift-key strokes(L/R): {0} ({1}/{2}) ({3:F1}%/{4:F1}%)",
                TotalShiftStroke,
                LeftShiftStroke,
                RightShiftStroke,
                (LeftShiftStroke * 100d) / (LeftShiftStroke + RightShiftStroke),
                (RightShiftStroke * 100d) / (LeftShiftStroke + RightShiftStroke));
            outputText += Environment.NewLine
                + String.Format("Counted enter strokes: {0}, CountedEnter");
            outputText += Environment.NewLine
                + String.Format("Total Keystrokes except space (L/R): {0} ({1}/{2}) ({3:F1}%/{4:F1}%)",
                TotalGeneralKeyStroke + TotalShiftStroke,
                LeftKeyStroke,
                RightKeyStroke,
                (LeftKeyStroke * 100d) / (LeftKeyStroke + RightKeyStroke),
                (RightKeyStroke * 100d) / (LeftKeyStroke + RightKeyStroke));
            Int32 TotalFinger = 0;
            for (Int32 i = 0; i < 10; i++)
            {
                if (i != 4 && i != 5)
                    TotalFinger += FingerStrokeDistrib[i];
            }
            outputText += Environment.NewLine + " L: ";
            for (Int32 i = 3; i > -1; i--)
                outputText += String.Format("{0}:{1}:{2:F1}% ", -i + 5, FingerStrokeDistrib[i], 100d * FingerStrokeDistrib[i] / TotalFinger);
            outputText += Environment.NewLine + " R: ";
            for (Int32 i = 6; i < 10; i++)
                outputText += String.Format("{0}:{1}:{2:F1}% ", i - 4, FingerStrokeDistrib[i], 100d * FingerStrokeDistrib[i] / TotalFinger);
            outputText += Environment.NewLine + Environment.NewLine
                + String.Format("Counted space strokes: {0}", CountedSpace);
            outputText += Environment.NewLine
                + String.Format("Untreated characters: {0}", KeyUntreatedCharacterTotal[keyLayoutNum]);

            outputText += Environment.NewLine + Environment.NewLine +
                "Continuous one-hand strokes frequency distribution";
            for (Int32 i = 0; i < 2; i++)
            {
                outputText += Environment.NewLine + String.Format(" {0}:", i == 0 ? 'L' : 'R');
                for (Int32 i2 = 0; i2 < 12; i2++)
                    if (ContinuousSameHandStroke[i, i2] != 0)
                        outputText += 
                            String.Format(" {0}:{1}({2:F1}%)", 
                            i2 + 1,
                            (i2 + 1) * ContinuousSameHandStroke[i, i2],
                            100d * (i2 + 1) * ContinuousSameHandStroke[i, i2] / (i == 0 ? LeftKeyStroke : RightKeyStroke));
            }

            outputText += Environment.NewLine + Environment.NewLine
                + String.Format("Continuous one-key strokes (L/R) : {0} ({1}/{2})", TotalSameKeyStroke, LeftSameKeyStroke, RightSameKeyStroke);
            for (Int32 i = 0; i < 10; i++)
            {
                {
                    Boolean b = false;
                    for (Int32 i2 = 1; i2 <= 6; i2++)
                        if (ContinuousSameKeyStroke[i, i2] == 0)
                            b = true;
                    if (b == false)
                        continue;
                }
                Int32 total = 0;
                outputText += String.Format(" {0:D2}: ", i - 5 < 0 ? i - 5 : i - 4);
                for (Int32 i2 = 1; i2 <= 6; i2++)
                {
                    if (ContinuousSameKeyStroke[i, i2] != 0)
                    {
                        outputText += String.Format("{0}*{1}", i2, ContinuousSameKeyStroke[i, i2]);
                        total += ContinuousSameKeyStroke[i, i2] * i2;
                    }
                    if (i2 != 6 && ContinuousSameFingerStroke[i, i2 + 1] != 0)
                        outputText += '+';
                }
                outputText += String.Format(" = {0}", total);
                //outputText += String.Format(" = {0} ({1:F1}%)", total, 100d*SameFingerStrokeDistrib[i] / Totalsamefinger
            }
            outputText += Environment.NewLine + Environment.NewLine
                + String.Format("Total Continuous one-finger strokes : {0} (L:{1} R:{2})",
                LeftSameFingerStroke + RightSameFingerStroke,
                LeftSameFingerStroke,
                RightSameFingerStroke);

            outputText += Environment.NewLine + Environment.NewLine
                + "Finger/Line frequency (Total key): "//모든 글쇠 손가락-줄 타수 통계
                + Environment.NewLine;
            for (Int32 i = 0; i < 10; i++)
            {
                if (i == 4 || i == 5) continue;
                if (i != 0) outputText += '\t';
                if (i < 4) outputText += String.Format("L{0}", Math.Abs(i - 5));
                else if (i > 5) outputText += String.Format("R{0}", i - 4);
            }
            outputText += Environment.NewLine;
            for (Int32 i = 5; i >= 2; i--)
            {
                for (Int32 i2 = 0; i2 < 10; i2++)
                {
                    if (i2 != 4 && i2 != 5)
                        outputText += String.Format("{0}\t", FingerLineFreq[i2, i]);
                }
                outputText += Environment.NewLine;
            }
            outputText += Environment.NewLine + Environment.NewLine
                + "Finger/Line frequency (General key): "//일반 글쇠 손가락-줄 타수 통계 (둘째 손가락 자리를 더 구분함)
                + Environment.NewLine;
            for (Int32 i = 0; i < 10; i++)
            {
                if (i != 0) outputText += '\t';
                if (i < 4) outputText += String.Format("L{0}", Math.Abs(i - 5));
                else if (i == 4) outputText += "L2-1";
                else if (i == 5) outputText += "R2-1";
                else if (i > 5) outputText += String.Format("R{0}", i - 4);
            }
            outputText += Environment.NewLine;
            for (Int32 i = 5; i > 1; i--)
            {
                for (Int32 i2 = 0; i2 < 10; i2++)
                {
                    if (i2 != 0)
                        outputText += '\t';
                    outputText += String.Format("{0}", GeneralKeyPositionFreq[i2, i]);
                }
                outputText += Environment.NewLine;
            }

            return outputText;
        }

        Int32[] ParseHan(Char c)
        {
            /* 한글 상용 조합형 인코딩 전용 연산
             * 유니코드 연산으로 바꿀 예정
             * http://ko.wikipedia.org/wiki/%EC%A1%B0%ED%95%A9%ED%98%95
             * http://ko.wikipedia.org/wiki/%ED%95%9C%EA%B8%80_%EC%83%81%EC%9A%A9_%EC%A1%B0%ED%95%A9%ED%98%95_%EC%9D%B8%EC%BD%94%EB%94%A9
             */
            Int32 flag = (c / 0x100) & 128;//10000000 00000000
            Int32 cho = ((c / 0x100) & (64 + 32 + 16 + 8 + 4)) >> 2; //01111100 00000000
            Int32 jung = (((c / 0x100) & (2 + 1)) << 3) | (((c % 0x100) & (128 + 64 + 32)) >> 5);//00000011 11100000
            Int32 jong = ((c % 0x100) & (1 + 2 + 4 + 8 + 16));//00000000 00011111
            return new Int32[] { flag, cho, jung, jong };
        }

        /* 한글 자소 코드
         * 초성: NL(1) ㄱ(2) ㄲ(3) ㄴ(4) ㄷ(5) ㄸ(6) ㄹ(7) ㅁ(8) ㅂ(9) ㅃ(10) ㅅ(11) ㅆ(12)
         * ㅇ(13) ㅈ(14) ㅉ(15) ㅊ(16) ㅋ(17) ㅌ(18) ㅍ(19) ㅎ(20)
         */
        readonly Char[] Cho = new Char[21]
        {
            '\0', '\0',
            'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ',
            'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
        };

        /*
         * 중성: NL(2) ㅏ(3) ㅐ(4) ㅑ(5) ㅒ(6) ㅓ(7) ㅔ(10) ㅕ(11) ㅖ(12) ㅗ(13) ㅘ(14)
         * ㅙ(15) ㅚ(18) ㅛ(19) ㅜ(20) ㅝ(21) ㅞ(22) ㅟ(23) ㅠ(26) ㅡ(27) ㅢ(28) ㅣ(29)
         */
        readonly Char[] Jung = new Char[30]
        {
            '\0', '\0', '\0',
            'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', '\0', '\0',
            'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ',
            'ㅙ', '\0', '\0', 'ㅚ',
            'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', '\0', '\0',
            'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ'
        };

        /*
         * 종성: NL(1) ㄱ(2) ㄲ(3) ㄳ(4) ㄴ(5) ㄵ(6) ㄶ(7) ㄷ(8) ㄹ(9) ㄺ(10) ㄻ(11) ㄼ(12)
         * ㄽ(13) ㄾ(14) ㄿ(15) ㅀ(16) ㅁ(17) ㅂ(19) ㅄ(20) ㅅ(21) ㅆ(22) ㅇ(23) ㅈ(24)
         * ㅊ(25) ㅋ(26) ㅌ(27) ㅍ(28) ㅎ(29)
         */
        readonly Char[] Jong = new Char[30]
        {
            '\0', '\0',
            'ㄱ','ㄲ','ㄳ','ㄴ','ㄵ','ㄶ','ㄷ','ㄹ','ㄺ','ㄻ','ㄼ',
            'ㄽ','ㄾ','ㄿ','ㅀ','ㅁ', '\0',
            'ㅂ','ㅄ','ㅅ','ㅆ','ㅇ','ㅈ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'
        };

        // 1바이트 특수기호, 숫자 ! @ # $ % ^ & *
        readonly Char[] OneByteSpecial = new Char[]
        {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            '`', '~',
            '!', '@', '#', '$', '%',
            '^', '&', '*', '(', ')',
            '-', '_', '=', '+',
            '[', ']', '{', '}', '\\', '|',
            ';', ':', '\'', '\"',
            ',', '.', '<',  '>', '/', '?'
        };

        readonly Char[] TwoByteSpecial = new Char[]
        {
            '·', '…', '〃', '―',
            '‘', '’', '“', '”', '※',
            '〈','〉', '《','》','「', '」', '『', '』', '【', '】',
            '±', '×', '÷', '≠',
            '°', '′', '″', '℃', '℉', '™',
	
            '○', '□',	'△', '▽', '◁', '▷',
            '→', '←', '↑', '↓', '↔',
            '☆', '♡', '☎', '☜', '☞',
            '￥', '€', '®', '©', 'ⓒ'
        };

        readonly Char[] TwoByteSpecialCode = new Char[]
        {
            '\uD934', '\uD936', '\uD938', '\uD93A',
            '\uD93E', '\uD93F', '\uD940', '\uD941', '\uD968',
            '\uD944', '\uD945', '\uD946', '\uD947', '\uD948', '\uD949', '\uD94A', '\uD94B',
            '\uD94E', '\uD94F', '\uD950', '\uD951',
            '\uD956', '\uD957', '\uD958', '\uD959', '\uD9B5', '\uD9E2',

            '\uD96B', '\uD970', '\uD972', '\uD974', '\uD9B7', '\uD9B9',
            '\uD976', '\uD977', '\uD978', '\uD979', '\uD97A',
            '\uD969', '\uD9BD', '\uD9CF', '\uD9D0', '\uD9D1',
            '\uD95D', '\uD9E6', '\uD9E7', '\uDCCF'
        };

        /* 닿소리 모음
         * NL(1) ㄱ(2) ㄲ(3) ㄳ(4) ㄴ(5) ㄵ(6) ㄶ(7) ㄷ(8) ㄸ(9) ㄹ(10) ㄺ(11) ㄻ(12) ㄼ(13) ㄽ(14) ㄾ(15) ㄿ(16) ㅀ(17) ㅁ(18)
         * ㅂ(19) ㅃ(20) ㅄ(21) ㅅ(22) ㅆ(23) ㅇ(24) ㅈ(25) ㅉ(26) ㅊ(27) ㅋ(28) ㅌ(29) ㅍ(30) ㅎ(31)
         */
        readonly Char[] Dah = new Char[]
        {
            '\0', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ',
            'ㅂ', 'ㅃ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
        };
        readonly Char[] HotDah = new Char[]
        {
            '\0', 'ㄱ', 'ㄴ', 'ㄷ', 'ㄹ', 'ㅁ',
            'ㅂ', 'ㅅ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
        };
        readonly Char[] DahKey = new Char[]
        {
            '\0', 'ㄱ', 'ㄴ', 'ㄷ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
        };

        /*
         * 중성: NL(2) ㅏ(3) ㅐ(4) ㅑ(5) ㅒ(6) ㅓ(7) ㅔ(10) ㅕ(11) ㅖ(12) ㅗ(13) ㅘ(14)
         * ㅙ(15) ㅚ(18) ㅛ(19) ㅜ(20) ㅝ(21) ㅞ(22) ㅟ(23) ㅠ(26) ㅡ(27) ㅢ(28) ㅣ(29)
         */
        readonly Char[] Hol = new Char[]
        {
            '\0', 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', '\0', '\0', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ',
            'ㅙ', '\0', '\0', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', '\0', '\0', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ'
        };
        readonly Char[] HotHol = new Char[]
        {
            '\0', 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ',
            'ㅛ', 'ㅜ', 'ㅠ', 'ㅡ', 'ㅣ'
        };
        readonly Char[] HolKey = new Char[]
        {
            '\0', 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ',
            'ㅛ', 'ㅜ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ'
        };

        Nullable<Int32> IsDahsori(Char ch)
        {
            for (Int32 i = 0; i < Dah.Length; i++)
            {
                if (ch == Dah[i]) return i;
            }
            return null;
        }

        Nullable<Int32> IsHolsori(Char holCh)
        {
            for (Int32 i = 0; i < Hol.Length; i++)
            {
                if (holCh == Hol[i]) return i;
            }
            return null;
        }

        Nullable<Int32> DahsoriKey(Char dahCh)
        {
            for (Int32 i = 0; i < DahKey.Length; i++)
            {
                if (dahCh == DahKey[i]) return i;
            }
            return null;
        }

        Nullable<Int32> HolsoriKey(Char ch)
        {
            for (Int32 i = 0; i < HolKey.Length; i++)
            {
                if (ch == HolKey[i]) return i;
            }
            return null;
        }

        Nullable<Int32> IsHotDahsori(Char ch)
        {
            for (Int32 i = 0; i < HotDah.Length; i++)
            {
                if (ch == HotDah[i]) return i;
            }
            return null;
        }

        Nullable<Int32> IsHotHolsori(Char ch)
        {
            for (Int32 i = 0; i < HotHol.Length; i++)
            {
                if (ch == HotHol[i]) return i;
            }
            return null;
        }

        public Processor()
        {
            typeinfo[0, 0] = '\u0000';
        }

        //패러미터 및 결과 설명 http://pat.im/854
        //'buff'(버퍼)는 'input'에 대응
        /// <summary>
        /// 
        /// </summary>
        /// <param name="input">input document for analysis</param>
        /// <param name="keyboardLayouts">Layout name, layout data lines</param>
        /// <param name="optionBeol2">2벌 자판 통계</param>
        /// <param name="optionBeol3">3벌 자판 통계</param>
        /// <param name="optionAlphabetStat">이음 잦기(결합 빈도) 통계</param>
        /// <returns></returns>
        public String StringProcess(String input, Dictionary<String,String[]> keyboardLayouts, Boolean optionBeol2, Boolean optionBeol3, Boolean optionAlphabetStat, Boolean optionCombinationStat)
        {
            String outputText = "";
            Int32 Choseong, Jungseong, Jongseong;
            Int32? PrevChoseong = null, PrevJungseong = null, PrevJongseong = null;
            Int32? PrevKey, NextKey;

            Int32[] OneByteSpecialFreq = new Int32[OneByteSpecial.Length];
            Int32[] TwoByteSpecialFreq = new Int32[TwoByteSpecial.Length];

            Int32 HangulNatnae = 0;//한글 낱내 수
            Int32 Enter = 0, Space = 0;//줄바꿈, 빈칸 수
            Int32 TotalDigits = 0;//숫자 잦기

            Int32[] ChoFreq = new Int32[Cho.Length];
            Int32[] JungFreq = new Int32[Jung.Length];
            Int32[] JongFreq = new Int32[Jong.Length];

            Int32[,] JongToChoConsonantCombFreq//닿소리 이음 잦기(결합 빈도) [끝소리][첫소리]
                = new Int32[Jong.Length, Cho.Length];
            Int32[,] JungToJongConsonantCombFreq//가운뎃소리-끝소리 이음 잦기 [가운뎃소리][끝소리]
                = new Int32[Jung.Length, Jong.Length];
            Int32[,] DahKeyConsonantCombFreq//가운뎃소리-끝소리 이음 잦기 [가운뎃소리][끝소리]
                = new Int32[DahKey.Length, DahKey.Length];
            Int32[,] HolToDahKeyConsonantCombFreq//가운뎃소리-끝소리 글쇠 이음 잦기 [가운뎃소리 글쇠][끝소리 글쇠]
                = new Int32[HolKey.Length, DahKey.Length];

            //Boolean Option = false;//선택 사항이 있는지 없는지
            //Boolean OptionBeol2 = false;
            //Boolean OptionBeol3 = false;
            //Boolean OptionAlphabetStat = false;
            //Boolean OptionCombinationStat = false;
            //이 부분은 패러미터에서 처리. UI에서 체크박스로 체크하게 하며, 기본으로 모두 체크되어 있게 놓는다
            //아래 있을 옵션 체크 부분은 따라서 UI 부분에서 처리하게 함

            //여기 있을 문자열 받아들이는 부분도 마찬가지로 UI부분에서 처리한 뒤 패러미터로 문자열만 주기로

            //키보드 레이아웃 파일들 찾아 열기 - UI 부분에서 처리

            //파일 이름을 따로 저장하는 부분이 있으나 패러미터로 주어지도록 처리한다.

            Int32 KeyboardLayoutTotal = keyboardLayouts.Count;
            Char[,] KeyCho = new Char[KeyboardLayoutTotal, Cho.Length];//Cho 어레이와 연관되어, Cho 어레이에 있는 문자를 칠 수 있는 쿼티 자판의 글자가 같은 차례로 나열
            Char[,] KeyJung = new Char[KeyboardLayoutTotal, Jung.Length];//이쪽도 마찬가지
            Char[,] KeyJong = new Char[KeyboardLayoutTotal, Jong.Length];//마찬가지
            Char[,] KeyOneByteSpecial = new Char[KeyboardLayoutTotal, OneByteSpecial.Length];//역시나
            Char[,] KeyTwoByteSpecial = new Char[KeyboardLayoutTotal, TwoByteSpecial.Length];//당연히

            String[] KeyQwerty = new String[KeyboardLayoutTotal];//글을 쓰면서 친 쿼티 자판 글자대로 나열

            KeySetType = new Int32[KeyboardLayoutTotal];
            KeyFingering = new String[KeyboardLayoutTotal];
            KeyUntreatedCharacterTotal = new Int32[KeyboardLayoutTotal];

            //어레이 지정 부분은 for문 없이 이미 충족되었으므로 넘어간다

            String[] KeyboardLayoutFilenames = keyboardLayouts.Keys.ToArray();
            for (Int32 i = 0; i < KeyboardLayoutFilenames.Length; i++)//파일 하나 읽기
            {
                //이 부분은 좀 다름
                //자판 배열 파일은... 넌 이미 열려있다
                //읽기도 마찬가지로... 넌 이미 읽혀있다
                foreach (String line in keyboardLayouts[KeyboardLayoutFilenames[i]])//자판 파일을 한 줄씩 읽는다
                {
                    Char StrKey;
                    Char StrQwerty;

                    Int32 num = Int32.Parse(line);//atoi
                    if (num == 0)
                    {
                        String[] split = line.Split(':');//:을 기준으로 문자열을 끊는다.
                        if (line[0] == '/' && line[1] == '/') continue;
                        else if (split[0] == "set type")
                        {
                            KeySetType[i] = Int32.Parse(split[1]);
                            continue;
                        }
                        else if (split[0] == "fingering")
                        {
                            String fingering = split[1].Trim();
                            if (fingering[0] >= 'a' && fingering[0] <= 'z')
                            {
                                fingering = (Char)(fingering[0] - 32) + fingering.Substring(1);
                            }

                            //운지법을 지정하지 않았거나 분석기에서 지원하지 않는 운지법을 지정했다면 기본 운지법(쿼티 자판식)으로 분석한다.
                            if (fingering == "")
                                KeyFingering[i] = "Qwerty";
                            else if (fingering != "Pyojun" && fingering != "Gong" && fingering != "Gong3")
                                KeyFingering[i] = "Unknown";
                        }
                    }

                    {
                        String[] split = line.Split('\t');//탭으로 나뉜 문자열을 끊는다.
                        if (split.Length == 0 || num == 0) continue;

                        if (split.Length <= 1) continue;
                        StrKey = split[1][0];
                        //StrKey:낱소리, 기호에 대응하는 글쇠
                        //추후 JSON 포맷에서 U+XXXX 형 어레이로 바꿀 것
                        //또는 유니코드 포맷이 아닌 유니컴포즈 포맷을 위해 F0| 형 포맷 지원 생각

                        if (split.Length <= 2) continue;
                        StrQwerty = split[2][0];
                        //StrQwerty:쿼티 자판에 대응하는 글쇠
                    }

                    switch (num)
                    {
                        case 1://한글 첫소리일 때
                            {
                                for (Int32 j = 0; j < Cho.Length; j++)
                                    if (StrKey == Cho[j])
                                        KeyCho[i, j] = StrQwerty;
                                break;
                            }
                        case 2://한글 가운뎃소리일 때
                            {
                                for (Int32 j = 0; j < Jung.Length; j++)
                                    if (StrKey == Jung[j])
                                        KeyJung[i, j] = StrQwerty;
                                break;
                            }
                        case 3://한글 끝소리일 때
                            {
                                for (Int32 j = 0; j < Jong.Length; j++)
                                    if (StrKey == Jong[j])
                                        KeyJong[i, j] = StrQwerty;
                                break;
                            }
                        case 4://숫자, 기호일 때 (한글이 아닐 때)
                            {
                                if (StrKey < 0x100)
                                {
                                    for (Int32 j = 0; j < OneByteSpecial.Length; j++)
                                        if (StrKey == OneByteSpecial[j])
                                            KeyOneByteSpecial[i, j] = StrQwerty;
                                }
                                else
                                {
                                    for (Int32 j = 0; j < TwoByteSpecial.Length; j++)
                                        if (StrKey == OneByteSpecial[j])
                                            KeyTwoByteSpecial[i, j] = StrQwerty;
                                }
                                break;
                            }
                    }
                }
            }

            for (Int32 i = 0; i < input.Length; i++)
            {
                if (input[i] >= 0x80)
                {
                    //2바이트 특수기호인지 알아보고 특수기호를 셈한다.
                    Int32 j = 0;
                    for (; j < TwoByteSpecial.Length; j++)
                    {
                        if (input[i] == TwoByteSpecialCode[j]) // 2바이트 특수기호고 뭐고..C#은 애초에 2바이트가 기본인데 ㅡㅡ
                        {
                            TwoByteSpecialFreq[j]++;
                            for (Int32 k = 0; k < KeyboardLayoutTotal; k++)
                            {
                                if (KeyTwoByteSpecial[k, j] != '\0')//자판 배열에 있는 2바이트 특수기호일 때 - 
                                {
                                    switch (TwoByteSpecial[j])//열고 닫는 따옴표를 일반 자판에 있는 따옴표로 치는 것으로 한다.
                                    {
                                        case '‘':
                                            {
                                                KeyQwerty[k] += '\'';
                                                break;
                                            }
                                        case '’':
                                            {
                                                KeyQwerty[k] += '\'';
                                                break;
                                            }
                                        case '“':
                                            {
                                                KeyQwerty[k] += '\"';
                                                break;
                                            }
                                        case '”':
                                            {
                                                KeyQwerty[k] += '\"';
                                                break;
                                            }
                                        default:
                                            {
                                                KeyQwerty[k] += KeyTwoByteSpecial[k, j];
                                                //길이 1짜리 길이를 구하라니 의사양반 이게 무슨소리요
                                                break;
                                            }
                                    }
                                }
                                else
                                {// 판 배열에 없는 2바이트 특수기호일 때
                                    KeyQwerty[k] += '\u0018';//자판 배열에 없는 문자를 쓰지 않는 문자로 채워둔다. - 엄청나게 많은 문자가 있는 터치 키보드는 어쩔거요
                                    KeyUntreatedCharacterTotal[k]++;
                                }
                            }
                            break;
                        }
                    }

                    if (j != TwoByteSpecial.Length || IsHanja(input[i], input[i + 1]))
                    {
                        PrevChoseong = PrevJungseong = PrevJongseong = null;
                        //i++;
                        continue;
                    }

                    {
                        Int32[] ary = ParseHan(input[i]);
                        Choseong = ary[1];
                        Jungseong = ary[2];
                        Jongseong = ary[3];
                    }

                    if (Choseong >= Cho.Length || Jungseong >= Jung.Length || Jongseong >= Jong.Length)
                    {
                        PrevChoseong = PrevJungseong = PrevJongseong = null;
                        //i++;
                        continue;
                    }

                    HangulNatnae++;
                    ChoFreq[Choseong]++;
                    JungFreq[Jungseong]++;
                    JongFreq[Jongseong]++;

                    if (Jongseong != 0)//끝소리가 있을 때에 가운뎃소리와 끝소리의 이음 잦기를 셈한다.
                    {
                        {
                            Int32? Hol = HolsoriKey(Jung[Jungseong]);
                            if (Hol.HasValue)
                                PrevKey = Hol;
                            else
                            {
                                switch (Jung[Jungseong])
                                {
                                    case 'ㅘ':
                                        PrevKey = HolsoriKey('ㅏ'); break;
                                    case 'ㅙ':
                                        PrevKey = HolsoriKey('ㅙ'); break;
                                    case 'ㅚ':
                                        PrevKey = HolsoriKey('ㅚ'); break;
                                    case 'ㅝ':
                                        PrevKey = HolsoriKey('ㅝ'); break;
                                    case 'ㅞ':
                                        PrevKey = HolsoriKey('ㅞ'); break;
                                    case 'ㅟ':
                                        PrevKey = HolsoriKey('ㅟ'); break;
                                    case 'ㅢ':
                                        PrevKey = HolsoriKey('ㅢ'); break;
                                    default:
                                        PrevKey = 0; break;
                                }
                            }
                        }
                        {
                            Int32? Dah = DahsoriKey(Jong[Jongseong]);
                            if (Dah.HasValue)
                                NextKey = Dah;
                            else
                            {
                                switch (Jong[Jongseong])
                                {
                                    case 'ㄲ':
                                        NextKey = IsHolsori('ㄱ'); break;
                                    case 'ㅆ':
                                        NextKey = DahsoriKey('ㅅ'); break;
                                    case 'ㅉ':
                                        NextKey = DahsoriKey('ㅈ'); break;
                                    case 'ㄳ':
                                        NextKey = DahsoriKey('ㄱ'); break;
                                    case 'ㄵ':
                                        NextKey = DahsoriKey('ㄴ'); break;
                                    case 'ㄶ':
                                        NextKey = DahsoriKey('ㄴ'); break;
                                    case 'ㄺ':
                                        NextKey = DahsoriKey('ㄹ'); break;
                                    case 'ㄻ':
                                        NextKey = DahsoriKey('ㄹ'); break;
                                    case 'ㄼ':
                                        NextKey = DahsoriKey('ㄹ'); break;
                                    case 'ㄽ':
                                        NextKey = DahsoriKey('ㄹ'); break;
                                    case 'ㄾ':
                                        NextKey = DahsoriKey('ㄹ'); break;
                                    case 'ㄿ':
                                        NextKey = DahsoriKey('ㄹ'); break;
                                    case 'ㅀ':
                                        NextKey = DahsoriKey('ㄹ'); break;
                                    case 'ㅄ':
                                        NextKey = DahsoriKey('ㅂ'); break;
                                    default:
                                        NextKey = 0; break;
                                }
                            }
                        }

                        HolToDahKeyConsonantCombFreq[PrevKey.Value, NextKey.Value]++;
                        //null이면 어떻게 나오는거지;; = 저 시나리오대로면 '나올 일 없다' 왜냐면 null인거 체크까지도 따로 하니까..
                        JungToJongConsonantCombFreq[Jungseong, Jongseong]++;
                    }

                    if (Jongseong != 0 && IsHotDahsori(Jong[Jongseong]) == 0)//겹받침의 앞뒤 닿소리 글쇠 이음 잦기를 셈한다. (2벌식)
                    {
                        if (DahsoriKey(Jong[Jongseong]).HasValue)
                        {
                            PrevKey = null;
                            NextKey = null;
                        }
                        else
                        {
                            switch (Jong[Jongseong])
                            {
                                case 'ㄳ':
                                    PrevKey = DahsoriKey('ㄱ'); NextKey = DahsoriKey('ㅅ'); break;
                                case 'ㄵ':
                                    PrevKey = DahsoriKey('ㄴ'); NextKey = DahsoriKey('ㅈ'); break;
                                case 'ㄶ':
                                    PrevKey = DahsoriKey('ㄴ'); NextKey = DahsoriKey('ㅎ'); break;
                                case 'ㄺ':
                                    PrevKey = DahsoriKey('ㄹ'); NextKey = DahsoriKey('ㄱ'); break;
                                case 'ㄻ':
                                    PrevKey = DahsoriKey('ㄹ'); NextKey = DahsoriKey('ㅁ'); break;
                                case 'ㄼ':
                                    PrevKey = DahsoriKey('ㄹ'); NextKey = DahsoriKey('ㅂ'); break;
                                case 'ㄽ':
                                    PrevKey = DahsoriKey('ㄹ'); NextKey = DahsoriKey('ㅅ'); break;
                                case 'ㄾ':
                                    PrevKey = DahsoriKey('ㄹ'); NextKey = DahsoriKey('ㅌ'); break;
                                case 'ㄿ':
                                    PrevKey = DahsoriKey('ㄹ'); NextKey = DahsoriKey('ㅍ'); break;
                                case 'ㅀ':
                                    PrevKey = DahsoriKey('ㄹ'); NextKey = DahsoriKey('ㅎ'); break;
                                case 'ㅄ':
                                    PrevKey = DahsoriKey('ㅂ'); NextKey = DahsoriKey('ㅅ'); break;
                                default:
                                    PrevKey = null; NextKey = null; break;
                            }
                        }

                        if (PrevKey.HasValue && NextKey.HasValue)
                            DahKeyConsonantCombFreq[PrevKey.Value, NextKey.Value]++;
                    }

                    if (PrevJongseong.HasValue && Jong[PrevJongseong.Value] != '\0')//앞 낱내의 끝소리와 현재 낱내의 첫소리와의 글쇠 이음 잦기를 셈한다. (2벌식)
                    {
                        {
                            Int32? Dah = DahsoriKey(Jong[PrevJongseong.Value]);
                            if (Dah.HasValue)
                                PrevKey = Dah;
                            else
                            {
                                switch (Jong[PrevJongseong.Value])
                                {
                                    case 'ㄲ':
                                        PrevKey = DahsoriKey('ㄱ'); break;
                                    case 'ㅆ':
                                        PrevKey = DahsoriKey('ㅅ'); break;
                                    case 'ㄳ':
                                        PrevKey = DahsoriKey('ㅅ'); break;
                                    case 'ㄵ':
                                        PrevKey = DahsoriKey('ㅈ'); break;
                                    case 'ㄶ':
                                        PrevKey = DahsoriKey('ㅎ'); break;
                                    case 'ㄺ':
                                        PrevKey = DahsoriKey('ㄱ'); break;
                                    case 'ㄻ':
                                        PrevKey = DahsoriKey('ㅁ'); break;
                                    case 'ㄼ':
                                        PrevKey = DahsoriKey('ㅂ'); break;
                                    case 'ㄽ':
                                        PrevKey = DahsoriKey('ㅅ'); break;
                                    case 'ㄾ':
                                        PrevKey = DahsoriKey('ㅌ'); break;
                                    case 'ㄿ':
                                        PrevKey = DahsoriKey('ㅍ'); break;
                                    case 'ㅀ':
                                        PrevKey = DahsoriKey('ㅎ'); break;
                                    case 'ㅄ':
                                        PrevKey = DahsoriKey('ㅅ'); break;
                                    default:
                                        PrevKey = null; break;
                                }
                            }
                        }

                        {
                            Int32? Dah = DahsoriKey(Cho[Choseong]);
                            if (Dah.HasValue)
                                NextKey = Dah;
                            else
                            {
                                switch (Cho[Choseong])
                                {
                                    case 'ㄲ':
                                        NextKey = DahsoriKey('ㄱ'); break;
                                    case 'ㄸ':
                                        NextKey = DahsoriKey('ㄷ'); break;
                                    case 'ㅃ':
                                        NextKey = DahsoriKey('ㅂ'); break;
                                    case 'ㅆ':
                                        NextKey = DahsoriKey('ㅅ'); break;
                                    case 'ㅉ':
                                        NextKey = DahsoriKey('ㅈ'); break;
                                    default:
                                        NextKey = null; break;
                                }
                            }
                        }


                        if (PrevKey.HasValue && NextKey.HasValue)
                            DahKeyConsonantCombFreq[PrevKey.Value, NextKey.Value]++;
                        JongToChoConsonantCombFreq[PrevJongseong.Value, Choseong]++;
                    }

                    for (Int32 k = 0; k < KeyboardLayoutTotal; k++)//사용자 정의 자판 배열의 한글 낱소리들을 쿼티 배열로 옮긴다.
                    {
                        //이 위치의 코드는 불러오기 실패할 경우인데... 그런 건 없습니다 ㅎㅎ
                        KeyQwerty[k] += KeyCho[k, Choseong];
                        KeyQwerty[k] += KeyJung[k, Jungseong];
                        KeyQwerty[k] += KeyJong[k, Jongseong];
                    }

                    PrevChoseong = Choseong;
                    PrevJungseong = Jungseong;
                    PrevJongseong = Jongseong;
                    //i++; 이거 왜해요?;;
                }
                else//1바이트 문자일 때. 아 이런거 필요 없는데.. 쩝 나중에 고치죠.
                {
                    if (input[i] == ' ' || input[i] == '\n')//빈칸이거나 줄이 바뀔 때
                    {
                        if (input[i] == ' ')
                            Space++;
                        else
                            Enter++;
                        for (Int32 j = 0; j < KeyboardLayoutTotal; j++)
                        {
                            KeyQwerty[j] += input[i];
                        }

                        PrevChoseong = PrevJungseong = PrevJongseong = null;
                        continue;
                    }

                    {
                        Int32 j = 0;
                        for (; j < OneByteSpecial.Length; j++)
                            if (OneByteSpecial[j] == input[i])
                                break;
                        if (j == OneByteSpecial.Length)
                        {
                            PrevChoseong = PrevJungseong = PrevJongseong = null;
                            continue;
                        }
                        else // else 쓰면 되지 다시 체크하는 건 무슨 짓입니까!! 뭐 어쨌든 원바이트 특수문자 발견.
                        {
                            for (Int32 k = 0; k < KeyboardLayoutTotal; k++)
                            {
                                if (KeyOneByteSpecial[j, k] != '\0')
                                {
                                    KeyQwerty[k] += KeyOneByteSpecial[k, j];
                                }
                                else
                                {
                                    KeyQwerty[k] += (Char)0x18;//안쓰는 캐릭터라네요 음
                                    KeyUntreatedCharacterTotal[k]++;
                                }
                            }
                        }

                        OneByteSpecialFreq[j]++;//원바이트 특수문자 발견한 기록
                        if (OneByteSpecial[j] >= '0' && OneByteSpecial[j] <= '9')
                            TotalDigits++;//특수문자중에 숫자가 잇떠요 뿌우
                    }

                    PrevChoseong = null;
                    PrevJungseong = null;
                    PrevJongseong = null;
                    //이럴거면 왜 함수 맨 위에 전역 변수로 해놓은거죠
                    //왜죠
                    //지금 보긴 귀찮고 나중에 좀 스코프 낮춰야겠네요..
                }
            }

            if (optionAlphabetStat)
            {
                Int32 TotalCho = 0, TotalJung = 0, TotalJong = 0, TotalSigns = 0;
                for (Int32 i = 0; i < OneByteSpecial.Length; i++)
                    TotalSigns += OneByteSpecialFreq[i];
                TotalSigns -= TotalDigits;
                for (Int32 i = 0; i < TwoByteSpecial.Length; i++)
                    TotalSigns += TwoByteSpecialFreq[i];
                for (Int32 i = 2; i < Cho.Length; i++)
                    if (Cho[i] != '\0')
                        TotalCho += ChoFreq[i];
                for (Int32 i = 3; i < Jung.Length; i++)
                    if (Cho[i] != '\0')
                        TotalJung += JungFreq[i];
                for (Int32 i = 2; i < Jong.Length; i++)
                    if (Cho[i] != '\0')
                        TotalJong += JongFreq[i];

                outputText += Environment.NewLine +
                    String.Format("한글 낱내: {0}", HangulNatnae);
                outputText += Environment.NewLine +
                    String.Format(" (첫소리: {0}, 가운뎃소리: {1}, 끝소리: {2})", TotalCho, TotalJung, TotalJong);
                outputText += Environment.NewLine +
                    String.Format("Digits: {0}", TotalDigits);
                outputText += Environment.NewLine +
                    String.Format("Signs: {0}", TotalSigns);
                outputText += Environment.NewLine +
                    String.Format("Enter: {0}", Enter);
                outputText += Environment.NewLine +
                    String.Format("Space: {0}", Space);

                outputText += Environment.NewLine +
                    String.Format("첫소리 잦기: {0}", TotalCho);
                for (Int32 i = 2; i < Cho.Length; i++)
                {
                    if (Cho[i] != '\0' || ChoFreq[i] != 0)
                        outputText += Environment.NewLine
                            + String.Format("{0}:{1:D9} ({2:F2}%)", Cho[i], ChoFreq[i], 100d * ChoFreq[i] / TotalCho);
                }
                outputText += Environment.NewLine +
                    String.Format("가운뎃소리 잦기: {0}", TotalJung);
                for (Int32 i = 3; i < Jung.Length; i++)
                {
                    if (Jung[i] != '\0' || JungFreq[i] != 0)
                        outputText += Environment.NewLine
                            + String.Format("{0}:{1:D9} ({2:F2}%)", Jung[i], JungFreq[i], 100d * JungFreq[i] / TotalJung);
                }
                outputText += Environment.NewLine +
                     String.Format("끝소리 잦기: {0}", TotalJong);
                for (Int32 i = 3; i < Jong.Length; i++)
                {
                    if (Jong[i] != '\0' || JongFreq[i] != 0)
                        outputText += Environment.NewLine
                            + String.Format("{0}:{1:D9} ({2:F2}%)", Jong[i], JongFreq[i], 100d * JongFreq[i] / TotalJong);
                }
                outputText += Environment.NewLine +
                      String.Format("Digits: {0}", TotalDigits);
                for (Int32 i = 0; i < 10; i++)
                    outputText += Environment.NewLine
                            + String.Format(" {0}:{1} ", i, OneByteSpecialFreq[i]);
                for (Int32 i = 10; i < OneByteSpecial.Length; i++)
                    outputText += Environment.NewLine
                            + String.Format("{0} : {1}", OneByteSpecial[i], OneByteSpecialFreq[i]);//숫자, 특수기호의 잦기를 출력한다.
                for (Int32 i = 0; i < TwoByteSpecial.Length; i++)
                    outputText += Environment.NewLine
                            + String.Format("{0} : {1}", TwoByteSpecial[i], TwoByteSpecialFreq[i]);//2바이트 특수기호(·, ※ 따위)의 잦기를 출력한다.
            }

            if (optionCombinationStat)
            {
                outputText += Environment.NewLine +
                    "Ggeutsori-Cheotsori ieum";//끝소리-첫소리 이음 잦기
                outputText += Environment.NewLine + "**";
                for (Int32 i = 2; i < Cho.Length; i++)
                    outputText += String.Format(" {0}", Cho[i]);
                for (Int32 i = 2; i < Jong.Length; i++)
                {
                    outputText += Environment.NewLine +
                        Jong[i].ToString();
                    for (Int32 i2 = 2; i2 < Cho.Length; i2++)
                        outputText += String.Format(" {0}", JongToChoConsonantCombFreq[i, i2]);
                }

                outputText += Environment.NewLine +
                   "Dahsori Key Combination Frequncy";//닿소리 글쇠 결합 빈도 (2벌식 + ㅆ 받침)
                outputText += Environment.NewLine + "*";
                for (Int32 i = 1; i < DahKey.Length; i++)
                    outputText += String.Format(" {0}", DahKey[i]);
                for (Int32 i = 1; i < DahKey.Length; i++)
                {
                    outputText += Environment.NewLine +
                        DahKey[i].ToString();
                    for (Int32 i2 = 2; i2 < DahKey.Length; i2++)
                        outputText += String.Format(" {0}", DahKeyConsonantCombFreq[i, i2]);
                }

                outputText += Environment.NewLine +
                   "Dahsori Key Combination Frequncy (inordered)";//닿소리 글쇠 결합 빈도 (2벌식 + ㅆ 받침, 앞뒤 차례 없이)
                outputText += Environment.NewLine + "x";
                for (Int32 i = 1; i < DahKey.Length; i++)
                    outputText += String.Format(" {0}", DahKey[i]);
                for (Int32 i = 1; i < DahKey.Length; i++)
                {
                    outputText += Environment.NewLine +
                        DahKey[i].ToString();
                    for (Int32 i2 = 2; i2 < DahKey.Length; i2++)
                        if (i == i2)
                            outputText += String.Format(" {0}", DahKeyConsonantCombFreq[i, i2]);
                        else
                            outputText += String.Format(" {0}", DahKeyConsonantCombFreq[i, i2] + DahKeyConsonantCombFreq[i2, i]);
                }

                outputText += Environment.NewLine +
                   "Gaunde-Ggeut sori combination frequency";//가운뎃소리-끝소리 이음 잦기
                outputText += Environment.NewLine + "++";
                for (Int32 i = 3; i < Jung.Length; i++)
                    if (Jung[i] != '\0')
                        outputText += String.Format(" {0}", Jung[i]);
                for (Int32 i = 2; i < Jong.Length; i++)
                {
                    outputText += Environment.NewLine +
                        Jong[i].ToString();
                    for (Int32 i2 = 3; i2 < Jung.Length; i2++)
                        if (Jung[i] == '\0')
                            continue;
                        else
                            outputText += String.Format(" {0}", JungToJongConsonantCombFreq[i2, i]);
                }

                outputText += Environment.NewLine +
                   "Hol-Dah sori key combination frequency";//가운뎃소리-끝소리 이음 잦기
                outputText += Environment.NewLine + "++";
                for (Int32 i = 1; i < HolKey.Length; i++)
                    outputText += String.Format(" {0}", HolKey[i]);
                for (Int32 i = 1; i < Jong.Length; i++)
                {
                    outputText += Environment.NewLine +
                        DahKey[i].ToString();
                    for (Int32 i2 = 1; i2 < HolKey.Length; i2++)
                        outputText += String.Format(" {0}", HolToDahKeyConsonantCombFreq[i2, i]);
                }
            }

            if (optionBeol2)
            {
                for (Int32 i = 0; i < KeyboardLayoutTotal; i++)
                {
                    if (KeySetType[i] != 2) continue;

                    outputText += Environment.NewLine +
                        String.Format("*** Result of {0} ({1} Sets, {2} Fingering) ***", KeyboardLayoutFilenames[i], KeySetType[i], KeyFingering[i]);
                    InitTypeInfo(i);
                    outputText += Parse(KeyQwerty[i], i);
                }
            }

            if (optionBeol3)
            {
                for (Int32 i = 0; i < KeyboardLayoutTotal; i++)
                {
                    if (KeySetType[i] != 3) continue;

                    outputText += Environment.NewLine +
                        String.Format("*** Result of {0} ({1} Sets, {2} Fingering) ***", KeyboardLayoutFilenames[i], KeySetType[i], KeyFingering[i]);
                    InitTypeInfo(i);
                    outputText += Parse(KeyQwerty[i], i);
                }
            }

            return outputText;
        }
    }
}
